﻿using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Diagnostics;
using DemoApplication1;

namespace Ganaye.EmbeddedDb
{

    public class Program
    {
        public static void Main()
        {
            CreateIndividualsMultiThread();
            //TestBinaryWriters();
            //TestDiskWritingSpeed();

            Console.WriteLine("Done");
            Console.ReadKey();
        }

        delegate System.IO.Stream NewStream(string filepath);

        private static void TestDiskWritingSpeed()
        {
            for (int bufferSize = 1; bufferSize <= 0x40000; bufferSize *= 8)
            {
                TestDiskWritingSpeed(System.String.Format("FileStream\t{0}", bufferSize), (string filePath) =>
                {
                    return new System.IO.FileStream(
                        filePath,
                        System.IO.FileMode.Create,
                        System.IO.FileAccess.ReadWrite,
                        System.IO.FileShare.None,
                        bufferSize);
                });
                TestDiskWritingSpeed(System.String.Format("BufferedStream\t{0})", bufferSize), (string filePath) =>
                {
                    return new System.IO.BufferedStream(
                        new System.IO.FileStream(
                        filePath,
                        System.IO.FileMode.Create,
                        System.IO.FileAccess.ReadWrite,
                        System.IO.FileShare.None),
                        bufferSize);
                });
            }
        }

        private static void TestDiskWritingSpeed(string text, NewStream newStreamFunc)
        {
            string filePath = System.IO.Path.GetTempFileName();
            using (new LogTime(text))
            {
                using (var stream = newStreamFunc(filePath))
                {
                    byte[] buff = new Byte[RECORD_SIZE];
                    for (int i = 0; i < LARGE_RECORD_COUNT; i++)
                    {
                        stream.Write(buff, 0, RECORD_SIZE);
                    }
                }
            }
            System.IO.File.Delete(filePath);
        }


        const int RECORD_SIZE = 70;
#if DEBUG
        const int LARGE_RECORD_COUNT = 10 * 1000 * 1000;
        const int RECORD_COUNT = 1000 * 1000;
#else
        const int LARGE_RECORD_COUNT = 10 * 1000 * 1000;
        const int RECORD_COUNT =  100 * 1000;
#endif
        public static void Debug(object o)
        {
            Console.WriteLine(o);
        }

        public static void Test6()
        {

            using (var tbl1 = new Table<Order>(@"orders.dbf"))
            {
                Order order1 = tbl1.NewRecord();
                order1.OrderId = 123;
                order1.Total = 111.22;
                order1.Total = 222.44;
                Console.WriteLine(order1.Total);
            }
        }

        public static void Test5()
        {
            using (Table tbl1 = new Table<Record>(@"..\..\DemoApplication1\bin\t6\t6.DBF"))
            {
                foreach (Record client in tbl1)
                {
                    //var c = (((t6Proxy)(client))).CHARACTER;
                    //client["NUMERICAL"] = 12.34;
                    Console.WriteLine("CHARACTER:" + client["CHARACTER"]);
                    Console.WriteLine("DATE:" + client["DATE"]);
                    Console.WriteLine("NUMERICAL:" + client["NUMERICAL"]);
                    Console.WriteLine("FLOAT:" + client["FLOAT"]);
                    Console.WriteLine("LOGICAL:" + client["LOGICAL"]);
                    Console.WriteLine();//client["CHARACTER")
                }
                Record client1;
                client1 = tbl1.NewRecord();
                using (client1.BeginUpdates())
                {
                    client1["NUMERICAL"] = 12.3;
                    client1["CHARACTER"] = "Pascal GANAYE";
                    client1["DATE"] = new DateTime(1969, 05, 23);
                }
            }
        }

        public static void Test4()
        {
            using (Table tbl1 = new Table<Record>("client.dbf"))
            {
                var tableDefinition1 = new TableDefinition();
                tableDefinition1.Columns.Add(new ColumnDefinition("ClientCode", ColumnType.DBASE3_NUMBER) { FieldType = typeof(int) });
                tableDefinition1.Columns.Add(new ColumnDefinition("mColumnName", ColumnType.DBASE3_CHARACTER));
                tableDefinition1.Columns.Add(new ColumnDefinition("Dob", ColumnType.DBASE3_DATE) { Nullable = true });

                tbl1.BuildColumnsFromDefinitions(tableDefinition1);
                tbl1.InternalSetProxyType(typeof(ClientProxy));
                var client = tbl1.NewRecord();
                using (client.BeginUpdates())
                {
                    var code = ((ClientProxy)client).ClientCode;
                    var name = ((ClientProxy)client).Name;
                    var dob = ((ClientProxy)client).Dob;


                    var code2 = client["ClientCode"];
                    var name2 = client["mColumnName"];
                    var dob2 = client["Dob"];

                    ((ClientProxy)client).ClientCode = 123;
                    ((ClientProxy)client).Name = "Pascal GANAYE";
                    ((ClientProxy)client).Dob = new DateTime(1969, 05, 23);
                    ((ClientProxy)client).Dob = null;

                    client["ClientCode"] = 123;
                    client["mColumnName"] = "Pascal GANAYE";
                    client["Dob"] = new DateTime(1969, 05, 23);
                    client["Dob"] = null;
                }
                var client1 = tbl1.NewRecord();
                using (client1.BeginUpdates())
                {
                    client1["ClientCode"] = 123;
                    client1["mColumnName"] = "Pascal GANAYE";
                    client1["Dob"] = new DateTime(1969, 05, 23);
                }
            }
        }

        public static Table<Individual> tbl1;

        public static void CreateIndividuals()
        {
            using (new LogTime(string.Format("Create {0} Individuals (into a table)", RECORD_COUNT)))
            {
                var f = new System.IO.FileInfo("indiv.dbf");
                if (f.Exists) f.Delete();

                using (tbl1 = new Table<Individual>("indiv.dbf"))
                {
                    var t = DateTime.Now;
                    int n = 0;

                    for (int i = 0; i < RECORD_COUNT; i++)
                    {
                        if (DateTime.Now.Subtract(t).TotalMilliseconds > 1000)
                        {
                            t = t.AddSeconds(1);
                            Console.WriteLine(n + " modifiedRecords created");
                            n = 0;
                            //Console.ReadKey();
                        }
                        CreateOneIndiv(null);
                        n++;

                    }
                }
            }

        }

        public static void CreateIndividualsMultiThread()
        {
            using (new LogTime(string.Format("Create {0} Individuals (into a table)", RECORD_COUNT)))
            {
                var f = new System.IO.FileInfo("indiv.dbf");
                if (f.Exists) f.Delete();

                using (tbl1 = new Table<Individual>("indiv.dbf"))
                {
                    for (int i = 0; i < RECORD_COUNT; i++)
                    {
                        System.Threading.ThreadPool.QueueUserWorkItem(CreateOneIndiv);
                    }
                }

            }

        }

        private static void CreateOneIndiv(object o)
        {
            var newIndiv = tbl1.NewRecord();
            using (newIndiv.BeginUpdates())
            {
                bool male = RandomData.RandomBool();
                newIndiv.FIRSTNAME = (male ? RandomData.RandomMaleFirstname() : RandomData.RandomFemaleFirstname());
                string middleName = (male ? RandomData.RandomMaleFirstname() : RandomData.RandomFemaleFirstname());
                if (middleName != newIndiv.FIRSTNAME)
                    newIndiv.MIDDLENAME = middleName;
                newIndiv.LASTNAME = RandomData.RandomLastName();
                newIndiv.STATE = RandomData.RandomState();
                newIndiv.DOB = RandomData.RandomDob();
            }
        }


        public static void TestBinaryWriters()
        {
            var filename = System.IO.Path.GetTempFileName();
            for (int k = 0; k < 10; k++)
            {
                const string NAME = "Pascal GANAYE";
                const string ADDRESS = "51 new king street, second floor, BATH BA1 2BN, United Kingdom";

                using (new LogTime("UTF8"))
                {
                    using (var sw = new System.IO.StreamWriter(filename, false, System.Text.Encoding.UTF8))
                    {
                        for (int i = 0; i < RECORD_COUNT; i++)
                        {
                            sw.WriteLine(NAME);
                            sw.WriteLine(ADDRESS);
                        }
                    }
                }

                using (new LogTime("GetBytes"))
                {
                    var encoder = System.Text.Encoding.UTF8;

                    using (var sw = new System.IO.FileStream(filename, System.IO.FileMode.Create))
                    {
                        var b = new Byte[4096];
                        for (int i = 0; i < RECORD_COUNT; i++)
                        {
                            int len = encoder.GetBytes(NAME, 0, NAME.Length, b, 0);
                            sw.Write(b, 0, len);
                            len = encoder.GetBytes(ADDRESS, 0, ADDRESS.Length, b, 0);
                            sw.Write(b, 0, len);
                        }
                    }
                }

                using (new LogTime("BinaryWriter"))
                {
                    var encoder = System.Text.Encoding.UTF8;

                    using (var sw = new System.IO.BinaryWriter(new System.IO.FileStream(filename, System.IO.FileMode.Create), System.Text.Encoding.UTF8))
                    {
                        for (int i = 0; i < RECORD_COUNT; i++)
                        {
                            sw.Write(NAME);
                            sw.Write(ADDRESS);
                        }
                    }
                }

                using (new LogTime("ASCII"))
                {
                    using (var sw = new System.IO.StreamWriter(filename, false, System.Text.Encoding.ASCII))
                    {
                        for (int i = 0; i < RECORD_COUNT; i++)
                        {
                            sw.Write(NAME);
                            sw.Write(ADDRESS);
                        }
                    }
                }

                //using (new LogTime("UTF32"))
                //{
                //    using (var sw = new System.IO.StreamWriter("a.txt", false, System.Text.Encoding.UTF32))
                //    {
                //        for (int i = 0; i < RECORD_COUNT; i++)
                //        {
                //            sw.Write(NAME);
                //            sw.Write(ADDRESS);
                //        }
                //    }
                //}
                Console.WriteLine();
            }
            Console.ReadKey();
        }

        public class LogTime : IDisposable
        {
            private Stopwatch sw;
            private string mText;

            public LogTime(string text)
            {
                sw = Stopwatch.StartNew();
                mText = text;
            }

            void IDisposable.Dispose()
            {
                Console.WriteLine(mText + ":" + sw.ElapsedMilliseconds + " ms");
            }
        }

        public static void Test2()
        {
            using (var db = new Database())
            {
                for (int i = 0; i < 100; i++)
                {
                    var newClient = db.NewRecord<Client>();
                    using (newClient.BeginUpdates())
                    {
                        Console.WriteLine("Client Code:" + newClient.ClientCode);
                        Console.WriteLine("mColumnName:" + newClient.Name);
                        newClient.Name = "Pascal GANAYE";
                    }
                }
                //foreach (Client c in db.Table<Client>())
                //{
                //    Console.WriteLine(c.mColumnName);

                //    foreach (Order o in c.Orders)
                //    {
                //        foreach (OrderLine l in o.Lines)
                //        {
                //        }
                //    }
                //}
            }
        }


        public static void Test1()
        {
            AppDomain ad = AppDomain.CurrentDomain;

            //var ad = domain;

            // create a new dynamic assembly
            AssemblyName an = new AssemblyName();
            an.Name = "DynamicRandomAssembly";
            AssemblyBuilder ab = ad.DefineDynamicAssembly(
             an, AssemblyBuilderAccess.Run);

            // create a new module to hold code in the assembly
            ModuleBuilder mb = ab.DefineDynamicModule("RandomModule");

            // create a baseType in the module
            TypeBuilder tb = mb.DefineType(
             "DynamicRandomClass", TypeAttributes.Public, typeof(Client));

            // create a method of the baseType
            Type returntype = typeof(int);
            Type[] paramstype = new Type[0];
            MethodBuilder methb = tb.DefineMethod("DynamicRandomMethod",
             MethodAttributes.Public, returntype, paramstype);

            // generate the MSIL
            ILGenerator gen = methb.GetILGenerator();
            gen.Emit(OpCodes.Ldc_I4, 1);
            gen.Emit(OpCodes.Ret);

            // finish creating the baseType and make it available
            Type t = tb.CreateType();

            // create an instance of the new baseType
            Object o = Activator.CreateInstance(t);
            // create an empty arguments array
            Object[] aa = new Object[0];
            // get the method and invoke it
            MethodInfo m = t.GetMethod("DynamicRandomMethod");
            int i = (int)m.Invoke(o, aa);
            Console.WriteLine("Method {0} in Class {1} returned {2}",
                m, t, i);

        }
    }

    public abstract class Individual : Record
    {
        public abstract string FIRSTNAME { get; set; }
        public abstract string MIDDLENAME { get; set; }
        public abstract string LASTNAME { get; set; }
        public abstract string STATE { get; set; }
        public abstract DateTime DOB { get; set; }
    }
}

